using System;
using System.Collections.Generic;
using Firestarter.DB.Schema.BO;
using Firestarter.DB_DomainModel.Logic;
using FireStarterModeller.BO;
using FireStarterModeller.Test;
using Habanero.Base.Exceptions;
using NUnit.Framework;
using Rhino.Mocks;

namespace Firestarter.DB_DomainModel.Logic.Test
{
    [TestFixture]
    public class TestDBToDMUniqueConstraintImporter : TestBase
    {
        [Test]
        public void Test_Construct()
        {
            //---------------Set up test pack-------------------
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Test Result -----------------------
            Assert.IsNotNull(dbToDMUniqueConstraintImporter);
        }

        [Test]
        public void Test_Construct_WhenNullDMClass_ShouldThrowError()
        {
            //---------------Set up test pack-------------------
            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            try
            {
                IList<string> errorMessages;
                dbToDMUniqueConstraintImporter.Import(null, new DBKey(), out errorMessages);
                Assert.Fail("expected ArgumentNullException");
            }
                //---------------Test Result -----------------------
            catch (ArgumentNullException ex)
            {
                StringAssert.Contains("Value cannot be null", ex.Message);
                StringAssert.Contains("dmClass", ex.ParamName);
            }
        }

        [Test]
        public void Test_Import_WhenDBAlternateKeyIsNull_ShouldThrowError()
        {
            //---------------Set up test pack-------------------
            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            try
            {
                IList<string> errorMessages;
                dbToDMUniqueConstraintImporter.Import(new DMClass(), null, out errorMessages);
                Assert.Fail("expected ArgumentNullException");
            }
                //---------------Test Result -----------------------
            catch (ArgumentNullException ex)
            {
                StringAssert.Contains("Value cannot be null", ex.Message);
                StringAssert.Contains("dbKey", ex.ParamName);
            }
        }

        [Test]
        public void Test_Import_ShouldLinkUniqueConstrainToClass()
        {
            //---------------Set up test pack-------------------
            DBTableBuilder builder = new DBTableBuilder(new DBTable());
            DBColumn keyColumn = builder.AddUniqueKeyColumn("ExistingAKColumn");
            DBKey dbKey = builder.Table.Keys[0];
            DMClass dmClass = new DMClass();
            dmClass.Properties.Add(new DMProperty() { PropertyName = keyColumn.ColumnName, MappedDBColumnID = keyColumn.ColumnID });
            DBToDMUniqueConstraintImporter uniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dbKey.Columns.Count);
            //---------------Execute Test ----------------------
            IList<string> errorMessages;
            DMUniqueConstraint uniqueConstraint = uniqueConstraintImporter.Import(dmClass, dbKey, out errorMessages);
            //---------------Test Result -----------------------
            Assert.IsNotNull(uniqueConstraint);
            Assert.AreEqual(1, uniqueConstraint.Properties.Count);
            Assert.AreSame(dmClass, uniqueConstraint.Class);
        }

        [Test]
        public void Test_Import_WithNoExistingUniqueConstraint_NoColumns()
        {
            //---------------Set up test pack-------------------
            DBKey dbKey = new DBKey();
            DMClass dmClass = new DMClass();
            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(0, dbKey.Columns.Count);
            //---------------Execute Test ----------------------
            IList<string> errorMessages;
            DMUniqueConstraint dmUniqueConstraint = dbToDMUniqueConstraintImporter.Import(dmClass, dbKey, out errorMessages);
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmUniqueConstraint);
            Assert.AreEqual(0, dmUniqueConstraint.UniqueConstraintProperties.Count);
            Assert.AreSame(dmUniqueConstraint.MappedDBKey, dbKey);
            Assert.AreEqual(dmUniqueConstraint.MappedAlternateKeyID, dbKey.KeyID);
            Assert.IsNotNull(dmUniqueConstraint.Generated);
            Assert.IsFalse(dmUniqueConstraint.Generated.Value);
        }

        [Test]
        public void Test_Import_WithExistingUniqueConstraint_WhenNoColumns_ShouldClearExistingDMUniqueConstraintProperties()
        {
            //---------------Set up test pack-------------------
            DBKey dbKey = CreateDBAlternateKeyWithMappedDMUniqueConstratintWithOneProp();
            DMUniqueConstraint uniqueConstraint = dbKey.GetMappedDMUniqueConstraint();
            DMClass dmClass = uniqueConstraint.Class;

            dbKey.KeyColumns[0].MarkForDelete();

            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(0, dbKey.Columns.Count);
            Assert.AreEqual(1, uniqueConstraint.Properties.Count);
            //---------------Execute Test ----------------------
            IList<string> errorMessages;
            DMUniqueConstraint returnedUniqueConstraint = dbToDMUniqueConstraintImporter.Import(dmClass, dbKey, out errorMessages);
            //---------------Test Result -----------------------
            Assert.IsNotNull(returnedUniqueConstraint);
            Assert.AreSame(uniqueConstraint, returnedUniqueConstraint);
            Assert.AreEqual(0, uniqueConstraint.Properties.Count);
            Assert.AreSame(returnedUniqueConstraint.MappedDBKey, dbKey);
        }

        private DBKey CreateDBAlternateKeyWithMappedDMUniqueConstratintWithOneProp()
        {
            DBKey dbKey = new DBKey();
            DBColumn dbColumn = new DBColumn("Some Column");
            dbKey.KeyColumns.Add(new DBKeyColumn { Column = dbColumn });
            DMClass dmClass = CreateBusinessObjectWithPropertyForColumn(dbColumn);
            dmClass.MappedTable = new DBTable();
            dbKey.Table = (DBTable)dmClass.MappedTable;
            DMUniqueConstraint uniqueConstraint = new DMUniqueConstraint(){MappedDBKey = dbKey};
            dmClass.UniqueConstraints.Add(uniqueConstraint);
            uniqueConstraint.AddPropertyInfo(dbColumn.GetMappedDMProperty());
            return dbKey;
        }

        [Test]
        public void Test_Import_WithNoExistingUniqueConstraint_OneColumns()
        {
            //---------------Set up test pack-------------------
            DBKey dbKey = new DBKey();
            DBColumn dbColumn = new DBColumn("Some Column");
            DMClass dmClass = CreateBusinessObjectWithPropertyForColumn(dbColumn);
            dbKey.AddColumnInfo(dbColumn);
            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dbKey.Columns.Count);
            AssertBusinessObjectContainsPropertyForColumn(dmClass, dbColumn);
            //---------------Execute Test ----------------------
            IList<string> errorMessages;
            DMUniqueConstraint dmUniqueConstraint = dbToDMUniqueConstraintImporter.Import(dmClass, dbKey, out errorMessages);
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmUniqueConstraint);
            Assert.AreEqual(1, dmUniqueConstraint.UniqueConstraintProperties.Count);
            DMProperty expectedBoProp = dmClass.Properties[0];
            DMProperty actualBoProp = dmUniqueConstraint.UniqueConstraintProperties[0].Property;
            Assert.IsNotNull(actualBoProp);
            Assert.AreSame(expectedBoProp, actualBoProp);
        }

        [Test]
        public void Test_Import_WithExistingUniqueConstraint_WhenOneDifferentColumn_ShouldUpdateUniqueConstraintProperty()
        {
            //---------------Set up test pack-------------------
            DBKey dbKey = CreateDBAlternateKeyWithMappedDMUniqueConstratintWithOneProp();
            DMUniqueConstraint uniqueConstraint = dbKey.GetMappedDMUniqueConstraint();
            DMClass dmClass = uniqueConstraint.Class;
            DBTable table = (DBTable)dbKey.Table;

            DBColumn newAKColumn = new DBColumn("NewAKColumn");
            table.Columns.Add(newAKColumn);
            dbKey.KeyColumns[0].Column = newAKColumn;

            DMProperty newAKProperty = new DMProperty { PropertyName = "NewAKProperty" };
            newAKProperty.MappedDBColumn = newAKColumn;
            newAKProperty.Class = dmClass;

            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            Assert.AreSame(uniqueConstraint, dmClass.UniqueConstraints[0]);
            Assert.AreEqual(1, dbKey.Columns.Count);
            Assert.AreNotSame(newAKProperty, uniqueConstraint.Properties[0]);
            //---------------Execute Test ----------------------
            IList<string> errorMessages;
            DMUniqueConstraint returnedUniqueConstraint = dbToDMUniqueConstraintImporter.Import(dmClass, dbKey, out errorMessages);
            //---------------Test Result -----------------------
            Assert.AreSame(uniqueConstraint, returnedUniqueConstraint);
            Assert.AreSame(uniqueConstraint, dmClass.UniqueConstraints[0]);
            Assert.AreEqual(1, uniqueConstraint.Properties.Count);
            Assert.AreSame(newAKProperty, uniqueConstraint.Properties[0]);
        }

        [Test]
        public void Test_Import_WithNoExistingUniqueConstraint_TwoColumns()
        {
            //---------------Set up test pack-------------------
            DBKey dbKey = new DBKey();
            DBColumn dbColumn = new DBColumn("Some Column");
            DBColumn dbColumnInfo2 = new DBColumn("Some Column1");
            DMClass dmClass = CreateBusinessObjectWithPropertyForTwoColumn(dbColumn, dbColumnInfo2);
            dbKey.AddColumnInfo(dbColumn);
            dbKey.AddColumnInfo(dbColumnInfo2);
            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(2, dbKey.Columns.Count);
            AssertBusinessObjectContainsPropertyForColumn(dmClass, dbColumn);
            //---------------Execute Test ----------------------
            IList<string> errorMessages;
            DMUniqueConstraint dmUniqueConstraint = dbToDMUniqueConstraintImporter.Import(dmClass, dbKey, out errorMessages);
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmUniqueConstraint);
            Assert.AreEqual(2, dmUniqueConstraint.Properties.Count);
            DMProperty expectedBoProp = dmClass.Properties[0];
            Assert.IsTrue(dmUniqueConstraint.Properties.Contains(expectedBoProp));
            DMProperty expectedBoProp2 = dmClass.Properties[1];
            Assert.IsTrue(dmUniqueConstraint.Properties.Contains(expectedBoProp2));
        }

        [Test]
        public void Test_Import_WithNoExistingUniqueConstraint_ShouldImportKeyName()
        {
            //---------------Set up test pack-------------------
            DBKey dbKey = new DBKey();
            dbKey.KeyName = TestUtilsShared.GetRandomString();
            DBColumn dbColumn = new DBColumn("Some Column");
            DMClass dmClass = CreateBusinessObjectWithPropertyForColumn(dbColumn);
            dbKey.AddColumnInfo(dbColumn);
            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dbKey.Columns.Count);
            AssertBusinessObjectContainsPropertyForColumn(dmClass, dbColumn);
            //---------------Execute Test ----------------------
            IList<string> errorMessages;
            DMUniqueConstraint dmUniqueConstraint = dbToDMUniqueConstraintImporter.Import(dmClass, dbKey, out errorMessages);
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmUniqueConstraint);
            Assert.AreEqual(dbKey.KeyName, dmUniqueConstraint.UniqueConstraintName);
        }

        [Test]
        public void Test_Import_WithNoExistingUniqueConstraint_ColumnsNotInBo()
        {
            //---------------Set up test pack-------------------
            DBKey dbKey = new DBKey {KeyName = "Some Key"};
            DBColumn dbColumn = new DBColumn("Some Column");
            DBColumn dbColumnInfo2 = new DBColumn("Some Column1");
            DMClass dmClass = CreateBusinessObjectWithPropertyForTwoColumn(dbColumn, dbColumnInfo2);
            DBColumn dbColumnNotInBo = new DBColumn("unmatched column");
            dbKey.AddColumnInfo(dbColumnNotInBo);
            DBToDMUniqueConstraintImporter dbToDMUniqueConstraintImporter = new DBToDMUniqueConstraintImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dbKey.Columns.Count);
            AssertBusinessObjectContainsPropertyForColumn(dmClass, dbColumn);
            //---------------Execute Test ----------------------
            IList<string> errorMessages;
            DMUniqueConstraint dmUniqueConstraint = dbToDMUniqueConstraintImporter.Import(dmClass, dbKey, out errorMessages);
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmUniqueConstraint);
            Assert.AreEqual(1, errorMessages.Count);
            string message = errorMessages[0];
            string expectedMessage = string.Format("The Unique Constraint '{0}' for business object '{1}' could not be "
                   + "created since the property for column '{2}' does not exist on the business object",
                   dbKey.KeyName, dmClass.ClassNameBO, dbColumnNotInBo.ColumnName);
            StringAssert.Contains(expectedMessage, message);
        }

        private static DMClass CreateBusinessObjectWithPropertyForColumn(DBColumn dbColumn)
        {
            DMClass dmClass = new DMClass();
            IDBToDMPropertyUpdater dbToDMPropertyUpdater = new DBToDMPropertyUpdater(MockRepository.GenerateStub<IDBTypeToDMPropertyTypeMapper>());
            DBToDMPropertyImporter dbToDMPropertyImporter = new DBToDMPropertyImporter(dbToDMPropertyUpdater);
            dbToDMPropertyImporter.Import(dmClass, dbColumn);
            return dmClass;
        }

        private static DMClass CreateBusinessObjectWithPropertyForTwoColumn(DBColumn dbColumn, DBColumn dbColumnInfo2)
        {
            DMClass dmClass = new DMClass();
            IDBToDMPropertyUpdater dbToDMPropertyUpdater = new DBToDMPropertyUpdater(MockRepository.GenerateStub<IDBTypeToDMPropertyTypeMapper>());
            DBToDMPropertyImporter dbToDMPropertyImporter = new DBToDMPropertyImporter(dbToDMPropertyUpdater);
            dbToDMPropertyImporter.Import(dmClass, dbColumn);
            dbToDMPropertyImporter.Import(dmClass, dbColumnInfo2);
            return dmClass;
        }

        private static void AssertBusinessObjectContainsPropertyForColumn
            (DMClass dmClass, DBColumn info)
        {
            bool found = false;
            foreach (DMProperty propertyInfo in dmClass.Properties)
            {
                if (propertyInfo.MappedDBColumnID != info.ColumnID) continue;
                found = true;
                break;
            }
            string message = string.Format
                ("The business object property for column {0} " + "was not found for business object {1}",
                 info.ColumnName, dmClass);
            Assert.IsTrue(found, message);
        }
    }
}